﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;

namespace JQuery
{
    public enum SerializeAs
    {
        String,
        Is
    }

    public delegate string Render(object itemToRender);

    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Field, Inherited = true)]
    public class JQueryElementAttribute : Attribute
    {

        public const string SELECTOR_PATTERN = @"((((?<selectorType>\.|#|\$)(?<selector>\w+))|(\*|span|div|input|option|select|textarea|p|h[0-9]|body|html))(\[\w(=\w)?\])?(?<function>\:(first|last|even|odd|((eq|gt|lt)\([0-9]+\))|header|animated|((not|has)\((?<selectorParameter>.+)\))))?( ?(\>|\+|\~) ?)?)+";

        public static Regex Any = new Regex(".*");
        public static Regex SelectorRegex = new Regex(SELECTOR_PATTERN);
        public static string VariableSelectorRegexPattern = SELECTOR_PATTERN.Replace("|html", "|html|{0}");

        public string JQueryElementName { get; set; }
        public string PropertyName { get; set; }
        public bool IsSelector { get; set; }
        public SerializeAs SerializeAs { get; set; }
        public Regex RegexValidation { get; set; }


        /// <summary>
        /// Declares this element to be a jquery element
        /// </summary>
        public JQueryElementAttribute()
            : this(null, null, false, SelectorRegex)
        {
        }

        /// <summary>
        /// Declares this element to be a jquery element with the given name
        /// </summary>
        public JQueryElementAttribute(string jQueryElementName)
            : this(null, jQueryElementName, false, Any)
        {
        }

        /// <summary>
        /// Declares this element to be a jquery element with the given name and the given regex to validate its value
        /// </summary>
        public JQueryElementAttribute(string jQueryElementName, Regex validation)
            : this(null, jQueryElementName, true, validation)
        {
        }

        public JQueryElementAttribute(bool isSelector)
            : this(null, null, isSelector, SelectorRegex)
        {
        }

        public JQueryElementAttribute(string jQueryElementName, bool isSelector)
            : this(null, jQueryElementName, isSelector, isSelector ? SelectorRegex : Any)
        {
        }

        protected JQueryElementAttribute(string jQueryElementName, SerializeAs serializeAs)
            : this(null, jQueryElementName, false, serializeAs, Any)
        {

        }

        public JQueryElementAttribute(string propertyName, string jQueryElementName, bool isSelector, SerializeAs serializeAs, string validationPattern)
            : this(propertyName, jQueryElementName, isSelector, serializeAs, isSelector ? new Regex(string.Format(VariableSelectorRegexPattern, validationPattern)) : new Regex(validationPattern))
        {
        }

        public JQueryElementAttribute(string propertyName, string jQueryElementName, bool isSelector, string validationPattern)
            : this(propertyName, jQueryElementName, isSelector, isSelector ? SerializeAs.String : SerializeAs.Is, validationPattern)
        {
        }


        public JQueryElementAttribute(string propertyName, string jQueryElementName, bool isSelector, Regex validation)
            : this(propertyName, jQueryElementName, isSelector, isSelector ? SerializeAs.String : SerializeAs.Is, validation)
        {
        }

        public JQueryElementAttribute(string propertyName, string jQueryElementName, bool isSelector, SerializeAs serializeAs, Regex validation)
        {
            PropertyName = propertyName;
            JQueryElementName = jQueryElementName;
            IsSelector = isSelector;
            SerializeAs = serializeAs;

            RegexValidation = validation;
        }

        internal bool IsMatch(string value)
        {
            return RegexValidation.IsMatch(value);
        }

        internal object GetValue(Control owner, object value)
        {
            this.owner = owner;
            if (!IsSelector || value == null)
                return value;
            return GetClientSelector(value.ToString());
        }

        private string GetClientSelector(string serverSelector)
        {
            return SelectorRegex.Replace(serverSelector, Replacer);
        }

        private Control owner;

        private string Replacer(Match match)
        {
            if (match.Groups["selectorType"].Value == "$")
            {
                return "#" + owner.FindControl(match.Groups["selector"].Value).ClientID;
            }
            return match.Value;
        }
    }
}
